#Python library imports
import itertools
#External libraries imports
from pyparsing import *
import theano
import theano.tensor as T
#Internal imports
from FSPTypesParsers import ConstantIdentifier
from ParsingUtilities import combine
__all__ = ("Expression")

theanoOperators = {
  "!": "T.eq({par}, 0)",
  "==": "T.eq({par1}, {par2})",
  "!=": "T.neq({par1}, {par2})",
  "&&": "T.switch(T.neq({par1},0), T.switch(T.neq({par2},0), 1, 0), 0)",
  "||": "T.switch(T.eq({par1},0), T.switch(T.eq({par2},0), 0, 1), 1)"}

def theanoChain(operators, x):
  theanoCode = x[0]
  theanoOperator = ""
  for b in x[1:]:
    if b in operators: theanoOperator  = theanoOperators[b]
    else: theanoCode = theanoOperator.format(par1=theanoCode, par2=b)
  return theanoCode

def chain(ex, op, act):
  parser = ex + ZeroOrMore(Or([(Literal(o) + ex) for o in op]))
  return parser.setParseAction(act) if act else parser

def tree(p): return p[0] if len(p) == 1 else chain(tree(p[1:]), p[0][0], p[0][1])

LowercaseIdentifier = Regex("[a-z][a-zA-Z0-9]*")

UppercaseIdentifier = Regex("[A-Z][a-zA-Z0-9]*")

Expression = Forward()

Integer = Word(nums)

BaseExpression = (Integer ^ ConstantIdentifier.copy().setParseAction(lambda x: str(x[0])) ^ LowercaseIdentifier ^
                  (Literal("(") + Expression + Literal(")"))
                  .setParseAction(combine))

Factor = (((Optional(Literal("+")) + BaseExpression)) ^
                   ((Literal("-") + BaseExpression)) ^
                   ((Suppress(Literal("!")) + BaseExpression)#todo: evitare di theanizzare se ho costanti
                   .setParseAction(lambda u:
                                     theanoOperators["!"].format(par=u[0]))))

Addition = tree([[["+", "-"], None], [["*", "/", "%"], None], Factor])

Shift = (Addition + ZeroOrMore((Suppress(Literal("<<")) + Addition)
                                .setParseAction(lambda x: ["*2**", x[0]]) ^
                               (Suppress(Literal(">>")) + Addition)
                                .setParseAction(lambda x: ["/2**", x[0]])))

Expression << tree([[["||"], lambda x: theanoChain(["||"], x)],
                    [["&&"], lambda x: theanoChain(["&&"], x)],
                    [["|"], combine],
                    [["^"], None],
                    [["&"], None],
                    [["==", "!="], lambda x: theanoChain(["==", "!="], x)],
                    [["<", "<=", ">", ">="], combine], Shift])

def evaluate(string):
  expression = eval(string)
  if type(expression) == int or type(expression) == bool: return int(expression)
  else: print("l'espressione incriminata è ", string, " e il suo tipo è ", type(expression))
  #symbolicExpression = theano.function(arguments, expression)#gli arguments sono iscalar associati alle var
  #tupleGenerator = itertools.product(*ranges)#i ranges sono i ranges associati alle var

Expression.setParseAction(lambda x: evaluate(x[0]))
